#include "../head/func.h"

#define FOREACH_ENTRY(TEMP, FUNC)                                           \
  if (TEMP->f_size == 0)                                                    \
    return 0;  \
  int maxsize = 1024 / sizeof(Entry);\
  int l_size = TEMP->f_size;  \
  int acount[1024/4];                                                 \
  int this_pos = 0;\
  int j = -1;\
  for (int i = 0; TEMP->i_zone[i] != -1; i++)                               \
  {                                                                         \
    if (l_size <= 0)                                                        \
    {                                                                       \
      cout << "internal error\n";                                           \
      return 0;                                                             \
    }                                                                       \
    if (i > 10)                                                             \
    {                                                                       \
      return 0;                                                             \
    }                                                                       \
    if (i < 10)                                                             \
    {                                                                      \
      int size = min(1024 / sizeof(Entry), l_size);   \
      this_pos = TEMP->i_zone[i];  \
      Entry entrybuf[size];                                                 \
      fseek(fp, superblock->data_base + 1024 * TEMP->i_zone[i], SEEK_SET);  \
      fread(entrybuf, sizeof(Entry), size, fp);                             \
      for (int k = 0; k < size; k++){\
        FUNC                                                                \
      }\
      l_size -= 1024 / sizeof(Entry);                                 \
    }                                                                       \
    else                                                                    \
    {    \
      fseek(fp, superblock->data_base + 1024 * TEMP->i_zone[10], SEEK_SET); \
      fread(acount, (TEMP->f_size/maxsize - 10 +1) * 4, 1, fp);                        \
      int size = min(1024 / sizeof(Entry), l_size);  \
      for ( j = 0; j < TEMP->f_size - 10; j++)                           \
      {                     \
        this_pos = acount[j] ;                                 \
        int size = min(1024 / sizeof(Entry), l_size);                       \
        Entry entrybuf[size];                                               \
        fseek(fp, superblock->data_base + 1024 * acount[j], SEEK_SET);      \
        fread(entrybuf, sizeof(Entry), size, fp);                           \
        for (int k = 0; k < size; k++){\
          FUNC                                                        \
        }                                    \
        l_size -= 1024 / sizeof(Entry);                               \
      }                                                                     \
    }                                                                       \
  }

int getParam(string &s)
{
  s = "";
  char c;
  while ((c = getchar()) == ' ')
    ;
  while (c != '\n' && c != ' ')
  {
    s += c;
    c = getchar();
  }

  if (c == '\n')
    return 0;
  return 1;
}

int parse(string &filename, Inode *stand, Inode *temp)
{
  if (filename[0] == '/')
  {
    if (filename.length() <= 1)
      return 0;
    filename = filename.substr(1, filename.length() - 1);
    return parse(filename, root, temp);
  }
  vector<string> v;
  // decompose string
  char *p = strtok((char *)filename.c_str(), "/");
  while (p)
  {
    v.push_back((string)p);
    p = strtok(nullptr, "/");
  }
  if (v[0] == ".")
    v.erase(v.begin());
  if (v.size() == 0)
    return 0;

  filename = v[v.size() - 1];
  memcpy(temp, stand, sizeof(Inode));
  if (v.size() == 1)
    return 1;
  // traverse
  int i = 1;
  while (i < v.size())
  {
    if (temp->f_type != Type::DIR)
      break;
    if (!findInode(temp, v[i - 1]))
      break;
    i++;
  }
  if (i == v.size() && temp->f_type == Type::DIR)
    return 1;
  return 0;
}

int findName(Inode *temp, string name)
{
  FOREACH_ENTRY(temp, {
    if (string(entrybuf[k].file_name) == name)
      return 1;
  });
  return 0;
}

int findInode(Inode *temp, string dirname)
{
  FOREACH_ENTRY(temp, {
    if (string(entrybuf[k].file_name) == dirname)
    {
      int inode_ind = entrybuf[k].inode_index;
      fseek(fp, superblock->inode_base + sizeof(Inode) * inode_ind, SEEK_SET);
      fread(temp, sizeof(Inode), 1, fp);
      return 1;
    }
  });
  return 0;
}

int buildInode(Mode mode, Type type, int blocknum,string filename)
{
  int inode_id = getAvaiInode();
  Inode *inode = new Inode(inode_id, mode, type);
  if (blocknum <= 10) {
    for (int i = 0; i < blocknum; i++)
    {
      inode->i_zone[i] = getAvaiData();
      fillblock(inode->i_zone[i]);
    }
  }
  else
  {
    for (int i = 0; i < 10; i++)
    {
      inode->i_zone[i] = getAvaiData();
      fillblock(inode->i_zone[i]);
    }
    inode->i_zone[10] = getAvaiData();
    int acount[1024/4];
    memset(acount, -1, sizeof(acount));
    for (int i = 0; i < blocknum - 10; i++)
    {
      acount[i] = getAvaiData();
      fillblock(acount[i]);
    }
    fseek(fp, superblock->data_base + 1024 * inode->i_zone[10], SEEK_SET);
    fwrite(acount, sizeof(acount), 1, fp);
   
  }
  if(type ==Type::DIR){
    blocknum-=1;
  }
  inode->f_size = blocknum;
  fseek(fp, superblock->inode_base + sizeof(Inode) * inode_id, SEEK_SET);
  fwrite(inode, sizeof(Inode), 1, fp);
  delete inode;
  return inode_id;
}

void addEntry(Inode *temp, Entry *entry)
{
  int maxsize = 1024 / sizeof(Entry); //其实entry的大小固定
  int blk = temp->f_size / maxsize;
  int entryid = temp->f_size % maxsize;
  int acount[1024/4];
  if(entryid==0&&blk!=0){
    blk-=1;
  }
  if (blk < 10)
  {
    if (temp->i_zone[blk] == -1)
      temp->i_zone[blk] = getAvaiData();
    fseek(fp,
          superblock->data_base + 1024 * temp->i_zone[blk] +
              sizeof(Entry) * entryid,
          SEEK_SET);
  }else{
    if(temp->i_zone[10]==-1){
      temp->i_zone[10] = getAvaiData();
    }
    fseek(fp, superblock->data_base + 1024 * temp->i_zone[10], SEEK_SET); 
    fread(acount, (blk-10+1) * 4, 1, fp);
    if(entryid == maxsize - 1){
      acount[blk-10+1] = getAvaiData();
    }
    fseek(fp, superblock->data_base + 1024 * temp->i_zone[10], SEEK_SET); 
    fwrite(acount, (blk-10+1) * 4, 1, fp);
    fseek(fp,
          superblock->data_base + 1024 * acount[blk-10] +
              sizeof(Entry) * entryid,
          SEEK_SET);    
  }
  fwrite(entry, sizeof(Entry), 1, fp);
  temp->f_size++;
  fseek(fp, superblock->inode_base + sizeof(Inode) * temp->id, SEEK_SET);
  fwrite(temp, sizeof(Inode), 1, fp);
}

int listInfo(Inode *stand)
{
  fseek(fp, superblock->inode_base + sizeof(Inode) * stand->id, SEEK_SET);
  fread(stand, sizeof(Inode), 1, fp);
  FOREACH_ENTRY(stand, {
    string filename = string(entrybuf[k].file_name);
    // if(filename ==".."){
    //     cout << setiosflags(ios::left) << setw(12) << filename << setw(8)
    //      << setw(8) << setw(8)  << setw(8) << endl;
    // }else{
      int inode_index = entrybuf[k].inode_index;
      Inode *inode = new Inode;
      fseek(fp, superblock->inode_base + sizeof(Inode) * inode_index, SEEK_SET);
      fread(inode, sizeof(Inode), 1, fp);
      int fsize = inode->f_size;
      if(inode->f_type==Type::DIR && inode->id != 0){
        fsize -=1;
      }
      cout << setiosflags(ios::left) << setw(12) << filename << setw(8)
           << typeToStr(inode->f_type) << setw(8) << modeToStr(inode->f_mode)
           << setw(8) << fsize << setw(8) << ctime(&inode->now) << endl;
      delete inode;
    //}

  });
  return 1;
}

//以下是史宣莉加的函数


int deleteEntry(Inode *temp, string name)
{
  FOREACH_ENTRY(temp, {
    if (string(entrybuf[k].file_name) == name)
    {
      //由于下一次写入fp时会覆盖，所以不用对fp上面的数据进行删除操作；
      //删除entry在目录里面的记录即可
      //有个问题 写入的时候时顺序写入，删除的时候不一定是顺序删除
      int blk = temp->f_size / maxsize;
      if (k == size - 1 && (i == blk || i == 10))
      {
        if (k == 0 && i != 0 && i != 10)
        {
          releaseData(temp->i_zone[blk]);
          temp->i_zone[blk] = -1;
        }
        if(k == 0 && i==10){
          if(j!=0){
            releaseData(acount[j]);
          }
          if(j==0){
            releaseData(acount[j]);
            releaseData(temp->i_zone[10]);
            temp->i_zone[10] = -1;
          }
        }
      }
      else
      {
        int entryid = temp->f_size % maxsize - 1;
        if (entryid < 0)
          {
            entryid = maxsize - 1;
          }
        Entry *t_entry = new Entry;
        int last_pos = 0;
        if(temp->f_size<=maxsize*10){
          last_pos = temp->i_zone[blk];
        }else{
          last_pos = acount[blk-10];
        }
        fseek(fp,
              superblock->data_base + 1024 * last_pos +
                  sizeof(Entry) * entryid,
              SEEK_SET);
        fread(t_entry, sizeof(Entry), 1, fp);
        fseek(fp,
              superblock->data_base + 1024 * this_pos +
                  sizeof(Entry) * k,
              SEEK_SET);
        fwrite(t_entry, sizeof(Entry), 1, fp);
        //还要考虑 最后一个是最后一个block的第一个位置；
        if (entryid == 0 && blk != 0)
        {
          if(blk-10<0){
            releaseData(temp->i_zone[blk]);
            temp->i_zone[blk] = -1;
          }else if(blk-10==0){
            releaseData(last_pos);
            releaseData(temp->i_zone[10]);
            temp->i_zone[10] = -1;
          }else{
            releaseData(last_pos);
          }
        }
      }
      temp->f_size -= 1;
      fseek(fp, superblock->inode_base + sizeof(Inode) * temp->id, SEEK_SET);
      fwrite(temp, sizeof(Inode), 1, fp);
      return 1;
    }
  });
  return 0;
}

void releaseData(int num)
{
  data_bit_map[num] = 0;
  fseek(fp, superblock->data_bitmap_base + num, SEEK_SET);
  char c = 0; //只分配一个block吗，如果文件大小超出了怎么办哎
  fwrite(&c, 1, 1, fp);
}

void releaseInode(int id)
{
  inode_bit_map[id] = 0;
  fseek(fp, superblock->inode_bitmap_base + id, SEEK_SET);
  char c = 0;
  fwrite(&c, 1, 1, fp);
}

int deleteInode(Inode *temp)
{
  if (temp->f_type == Type::REG)
  {
    if(temp->f_size<11){
      for (int i = 0; i < 10; i++){
        releaseData(temp->i_zone[i]);
      }
    }else{
      for (int i = 0; i < 10; i++){
        releaseData(temp->i_zone[i]);
      }
      int acount[1024/4];
      fseek(fp, superblock->data_base + 1024 * temp->i_zone[10], SEEK_SET); \
      fread(acount, (temp->f_size - 10 +1)*4, 1, fp);
      for(int i =0;i<(temp->f_size - 10)*4/1024;i++){
        releaseData(acount[i]);
      } 
      releaseData(temp->i_zone[10]);  
    }
  }
  else
  {//删除完里面的entry里面的inode，还要删除目录的datablock 和inode
    FOREACH_ENTRY(temp, {
      int id = entrybuf[k].inode_index;
      if (!(i == 0 && (k == 0 )))
      {
        Inode *inode = new Inode();
        fseek(fp, superblock->inode_base + sizeof(Inode) * id, SEEK_SET);
        fread(inode, sizeof(Inode), 1, fp);
        deleteInode(inode);
        delete inode;
      }
      if(k==0 && j>0){
        releaseData(acount[j]);
      }
      if(k==0&& j==0){
        releaseData(acount[j]);
        releaseData(temp->i_zone[10]);
      }
      if(k==0&&i<10){
        releaseData(temp->i_zone[i]);
      }
    });
  }
  releaseInode(temp->id);
  return 0;
}


void fillblock(int blockid) {
  char buf[1024];
  for(int i = 0; i < 1024; i++) 
    buf[i] = rand()%26+'A';
  fseek(fp, superblock->data_base+blockid*1024, SEEK_SET);
  fwrite(buf, 1024, 1, fp);
}

void cpdata(int source_block_id,int dest_block_id){
  char str[1024];
  fseek(fp, superblock->data_base + 1024 * source_block_id, SEEK_SET);
  fread(str, 1024, 1, fp);
  fseek(fp, superblock->data_base + 1024 * dest_block_id, SEEK_SET);
  fwrite(str, 1024, 1, fp);
}
//以上是史宣莉加的函数

bool updateIBMBuffer() { return true; }

int getAvaiInode()
{
  for (int i = 0; i < 1024; i++)
  {
    if (inode_bit_map[i] == 0)
    {
      inode_bit_map[i] = 1;
      fseek(fp, superblock->inode_bitmap_base + i, SEEK_SET);
      char c = 1;
      fwrite(&c, 1, 1, fp);
      return i;
    }
  }
  if (updateIBMBuffer())
    return getAvaiInode();
  return -1;
}

bool updateDBMBuffer() { return true; }

int getAvaiData()
{
  for (int i = 0; i < 1024; i++)
  {
    if (data_bit_map[i] == 0)
    {
      data_bit_map[i] = 1;
      fseek(fp, superblock->data_bitmap_base + i, SEEK_SET);
      char c = 1; //只分配一个block吗，如果文件大小超出了怎么办哎
      fwrite(&c, 1, 1, fp);
      return i;
    }
  }
  if (updateDBMBuffer())
    return getAvaiData();
  return -1;
}

string typeToStr(Type t)
{
  switch (t)
  {
  case Type::DIR:
    return string("DIR");
  case Type::REG:
    return string("REG");
  default:
    return string("REG");
  }
}

string modeToStr(Mode m)
{
  switch (m)
  {
  case Mode::R:
    return "R";
  case Mode::W:
    return "W";
  case Mode::RW:
    return "RW";
  }
  return "RW";
}